#include "ring_poly.h"
#include <cstring>

Poly::Poly() : Coeffs() {}

Poly::Poly(uint64_t N, uint64_t nbModuli)
{
    Coeffs.resize(nbModuli, std::vector<uint64_t>(N, 0));
}

Poly::~Poly() {}

int Poly::GetDegree() const
{
    if (Coeffs.empty()) {
        return 0;
    }
    return static_cast<int>(Coeffs[0].size());
}

void Poly::Zero()
{
    for (auto &mod_coeffs : Coeffs) {
        std::fill(mod_coeffs.begin(), mod_coeffs.end(), 0);
    }
}

Poly *Poly::CopyNew() const
{
    Poly *p1 = new Poly();
    p1->Coeffs.resize(Coeffs.size(), std::vector<uint64_t>());
    for (size_t i = 0; i < Coeffs.size(); ++i) {
        p1->Coeffs[i].resize(Coeffs[i].size());
        std::copy(Coeffs[i].begin(), Coeffs[i].end(), p1->Coeffs[i].begin());
    }
    return p1;
}

void Poly::Copy(Poly *p1)
{
    if (this != p1) {
        int minLevel = std::min(this->Level(), p1->Level());
        for (int i = 0; i < minLevel + 1; i++) {
            std::copy(p1->Coeffs[i].begin(), p1->Coeffs[i].end(), this->Coeffs[i].begin());
        }
    }
    this->isNTT = p1->isNTT;
}

int Poly::Level()
{
    return Coeffs.size() - 1;
}

void Poly::CopyValues(Poly *p1)
{
    if (this != p1) {
        int minLevel = std::min(Level(), p1->Level());
        for (int i = 0; i < minLevel + 1; i++) {
            for (int j = 0; j < Coeffs[i].size(); j++) {
                Coeffs[i][j] = p1->Coeffs[i][j];
            }
        }
    }
}

PolyQP::PolyQP(Poly *Q, Poly *P) : Q(Q), P(P) {}

Poly *NewPoly(uint64_t N, uint64_t nbModuli)
{
    Poly *pol = new Poly();
    pol->Coeffs.resize(nbModuli, std::vector<uint64_t>(N, 0));
    return pol;
}

void CopyValues(Poly *p0, Poly *p1)
{
    CopyValuesLvl(std::min(p0->Level(), p1->Level()), p0, p1);
}

void CopyValuesLvl(int level, Poly *p0, Poly *p1)
{
    if (p0 != p1) {
        for (int i = 0; i < level + 1; i++) {
            std::copy(p0->Coeffs[i].begin(), p0->Coeffs[i].end(), p1->Coeffs[i].begin());
        }
    }
}

void MapSmallDimensionToLargerDimensionNTT(Poly *polSmall, Poly *polLarge)
{
    int64_t gap = polLarge->Coeffs[0].size() / polSmall->Coeffs[0].size();
    for (uint64_t j = 0; j < polSmall->Coeffs.size(); j++) {
        std::vector<uint64_t> &tmp0 = polSmall->Coeffs[j];
        std::vector<uint64_t> &tmp1 = polLarge->Coeffs[j];
        for (uint64_t i = 0; i < polSmall->Coeffs[0].size(); i++) {
            uint64_t coeff = tmp0[i];
            for (int64_t w = 0; w < gap; w++) {
                tmp1[i * gap + w] = coeff;
            }
        }
    }
}